package com.easylinkin.linkappapi.grid.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.core.models.Operator;
import com.easylinkin.core.utils.HttpFileUtils;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.exceptions.ExceptionEnum;
import com.easylinkin.linkappapi.grid.entity.GridReportWeek;
import com.easylinkin.linkappapi.grid.enums.EGridFileBusSubDef;
import com.easylinkin.linkappapi.grid.enums.EGridFileBusType;
import com.easylinkin.linkappapi.grid.mapper.GridReportWeekMapper;
import com.easylinkin.linkappapi.grid.models.dto.GridReportWeekSaveDTO;
import com.easylinkin.linkappapi.grid.models.query.GridFileBatchQuery;
import com.easylinkin.linkappapi.grid.models.query.GridReportWeekQuery;
import com.easylinkin.linkappapi.grid.models.vo.GridFileInfoVO;
import com.easylinkin.linkappapi.grid.models.vo.GridReportWeekInfoVO;
import com.easylinkin.linkappapi.grid.models.vo.GridReportWeekListVO;
import com.easylinkin.linkappapi.grid.models.vo.GridReportWeekUserInfoVO;
import com.easylinkin.linkappapi.grid.service.IGridFileService;
import com.easylinkin.linkappapi.grid.service.IGridReportWeekService;
import com.easylinkin.linkappapi.grid.service.IGridReportWeekUserService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.IsoFields;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 网格周报表 服务实现类
 *
 * @author kuyujie
 * @since 2024-05-17
 */
@Slf4j
@Setter(onMethod_ = @Resource)
@Validated
@Service
public class GridReportWeekServiceImpl extends ServiceImpl<GridReportWeekMapper, GridReportWeek> implements IGridReportWeekService {

    private IGridFileService gridFileService;
    private IGridReportWeekUserService gridReportWeekUserService;

    @Override
    public IPage<GridReportWeekListVO> pageList(IPage<?> page, GridReportWeekQuery query) {
        return baseMapper.pageList(page, query);
    }

    @Override
    public byte[] export(GridReportWeekQuery query) {
        List<GridReportWeekListVO> list = baseMapper.listByQuery(query);
        if (CollUtil.isEmpty(list)) {
            return new byte[0];
        }

        Set<Long> reportIds = list.stream().map(GridReportWeekListVO::getId).collect(Collectors.toSet());

        Map<String, List<GridFileInfoVO>> fileInfoListMap = gridFileService.mapListInfoByBatchQuery(GridFileBatchQuery.builder()
                .busIds(reportIds)
                .busType(EGridFileBusType.REPORT_DAY.getCode())
                .busSubType(EGridFileBusSubDef.DEF.getCode())
                .build()
        );

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
        ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);

        list.parallelStream().forEach(item -> {
            try {
                // 签字扫描件
                String attachmentKey = item.getId() + "," + EGridFileBusType.REPORT_DAY.getCode() + "," + EGridFileBusSubDef.DEF.getCode();
                List<GridFileInfoVO> attachmentFileInfoList = fileInfoListMap.get(attachmentKey);
                if (CollUtil.isNotEmpty(attachmentFileInfoList)) {
                    for (GridFileInfoVO fileInfoVO : attachmentFileInfoList) {
                        try {
                            HttpResponse httpResponse = HttpFileUtils.requestDownload(fileInfoVO.getFileUrl(), 1000);
                            zipOutputStream.putNextEntry(new ZipEntry(item.getReportName() + File.separator + fileInfoVO.getFileName()));
                            zipOutputStream.write(httpResponse.bodyBytes());
                            zipOutputStream.closeEntry();
                        } catch (Exception e) {
                            log.error("下载文件失败 fileInfoVO:{}", JSON.toJSONString(fileInfoVO), e);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("导出失败 item:{}", JSON.toJSONString(item), e);
            }
        });

        // 总的zip ZipOutputStream close
        try {
            zipOutputStream.flush();
            //解决CRS校验失败问题
            zipOutputStream.finish();
            zipOutputStream.close();
            byteArrayOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return byteArrayOutputStream.toByteArray();
    }

    @Override
    public void saveData(@Validated GridReportWeekSaveDTO dto) {
        Operator operator = dto.getOperator();
        LocalDateTime submitDeadline = dto.getSubmitDeadline();
        // 计算并输出是当年的第几周
        int weekOfYear = submitDeadline.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
        // 获取本周第一天（周一）的开始时间，这里设置时间为当天的0点
        LocalDateTime startOfWeek = submitDeadline.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).withHour(0).withMinute(0).withSecond(0).withNano(0);
        // 获取本周最后一天（周日）的结束时间，这里设置时间为当天的最后一秒（23:59:59）
        LocalDateTime endOfWeek = submitDeadline.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)).withHour(23).withMinute(59).withSecond(59).withNano(999999999);

        String reportName = String.format("%s年第%s周(%s~%s)周报", endOfWeek.getYear(), weekOfYear,
                DateUtil.format(startOfWeek, "MM月dd日"), DateUtil.format(endOfWeek, "MM月dd日"));

        int existFlag = baseMapper.existByDate(operator.getTenantId(), dto.getSubmitDeadline());
        if (existFlag > 0) {
            log.error("周报设置重复");
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.DATA_CHECK_ERROR, "周报设置重复");
        }

        GridReportWeek entity = GridReportWeek.builder()
                .reportName(reportName)
                .weekStart(startOfWeek)
                .weekEnd(endOfWeek)
                .submitDeadline(dto.getSubmitDeadline())
                .requiresSubmitNum(1)
                .alreadySubmitNum(0)
                // 公共字段
                .tenantId(operator.getTenantId())
                .creatorId(operator.getUserId())
                .creator(operator.getNickname())
                .createTime(operator.getNow())
                .modifyId(operator.getUserId())
                .modifier(operator.getNickname())
                .modifyTime(operator.getNow())
                .build();

        boolean dbFlag = this.save(entity);
        if (!dbFlag) {
            log.error("周报设置数据保存失败");
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.EXCEPTION_CODE_500, "周报设置数据保存失败");
        }

        dbFlag = gridReportWeekUserService.saveData(entity, Collections.singletonList(dto.getUserId()));
        if (!dbFlag) {
            log.error("周报填报人数据保存失败");
            throw new BusinessException(ExceptionEnum.CommonExceptionEnum.EXCEPTION_CODE_500, "周报填报人数据保存失败");
        }
    }

    @Override
    public GridReportWeekInfoVO getInfo(Long id) {
        GridReportWeekInfoVO infoVO = baseMapper.selectInfoById(id);
        List<GridReportWeekUserInfoVO> userList = gridReportWeekUserService.listByReportId(id, infoVO.getSubmitDeadline());
        infoVO.setUserList(userList);
        return infoVO;
    }

}